/*
 * $Id: TagUtils.java 26 2012-01-19 21:59:06Z rook.alex@gmail.com $
 */
package ua.org.energy.vkek.site.server;

import com.google.appengine.api.datastore.Key;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import ua.org.energy.vkek.site.model.Article;
import ua.org.energy.vkek.site.model.ArticleCategory;
import ua.org.energy.vkek.site.model.ArticleTag;

/**
 * вставка/удаление/переименование тэгов статей на данный момент методы работают
 * в предположении, что имя тэга уникально для всех категорий и осуществляют
 * проверку этого факта.
 */
public class TagUtils {

    private EntityManager em;

    public TagUtils(EntityManager em) {
        this.em = em;
    }

    /**
     * Creates ArticleTag and attaches it to existing ArticleCategory
     *
     * @param categoryName Article Category name for attach Tag
     * @param tagName Tag name to create & attach to Category
     * @throws DataIOException
     */
    public ArticleTag attachToCategory(String categoryName, String tagName) throws DataIOException {
        ArticleTag tag = new ArticleTag();
        tag.setTagName(tagName);
        return attachToCategory(categoryName, tag);
    }

    public ArticleTag attachToCategory(String categoryName, ArticleTag tag) throws DataIOException {
        healtCheck(tag);
        checkUnique(tag.getTagName());
        return glueToCategory(categoryName, tag);
    }

    private ArticleTag glueToCategory(String categoryName, ArticleTag tag) throws DataIOException {
        healtCheck(tag);
        ArticleCategory target = CategoryUtils.findCategory(categoryName, em);
        if (target != null) {
            target.getArticleTags().add(tag);
            tag.setCategory(target);
            em.merge(tag);
            // em.persist(tag);
            return tag;
        } else {
            throw new DataIOException("could not find target Category");
        }
    }

    /**
     * Renames the Article Tag
     *
     * @param oldtagName old tag name
     * @param newtagName new tag name
     */
    public ArticleTag updateName(String oldtagName, String newtagName) throws DataIOException {
        checkUnique(newtagName);
        try {
            ArticleTag oldval = findArticleTag(oldtagName);
            if (oldval != null) {
                oldval.setTagName(newtagName);
                return oldval;
            } else {
                throw new DataIOException("could not find ArticleTag for update");
            }
        } catch (Exception e) {
            throw new DataIOException(e.getMessage());
        }

    }

    public void delete(String tagName) throws DataIOException {
        try {
            ArticleTag oldval = findArticleTag(tagName);
            if (oldval != null) {
                if (hasArticles(oldval)) {
                    throw new DataIOException("this tag has own one or more articles,remove them first");
                }
                em.remove(oldval);
            } else {
                throw new DataIOException("could not find ArticleTag for delete");
            }
        } catch (Exception e) {
            throw new DataIOException(e.getMessage());
        }

    }

    private boolean hasArticles(ArticleTag tag) {
        List<Article> orphaned = findArticles(tag);
        return (!orphaned.isEmpty());
    }

    public List<Article> findArticles(ArticleTag tag) {
        return (List<Article>) em.createNamedQuery("ArticleTag.findArticlesByTag").setParameter("tag", tag).getResultList();
    }

    public List<Article> findArticles(String tagName) throws DataIOException {
        ArticleTag tag = findArticleTag(tagName);
        if (tag != null) {
            return findArticles(tag);
        } else {
            throw new DataIOException("could not find tag with given name");
        }
    }

    public void incrementWeight(String tagName) throws DataIOException {
        ArticleTag tag = findArticleTag(tagName);
        if (tag != null) {
            tag.incWeight();
        } else {
            throw new DataIOException("could not find tag with given name");
        }
    }

    public void decrementWeight(String tagName) throws DataIOException {
        ArticleTag tag = findArticleTag(tagName);
        if (tag != null) {
            tag.decWeght();
        } else {
            throw new DataIOException("could not find tag with given name");
        }
    }

    public ArticleTag findArticleTag(String tagName) {
        return findArticleTag(tagName, this.em);
    }

    public static ArticleTag findArticleTag(String tagName, EntityManager em) {
        try {
            Object obj = em.createNamedQuery("ArticleTag.findByName").setParameter("tagName", tagName).getSingleResult();
            return (ArticleTag) obj;
        } catch (NoResultException e) {
            return null;
        }
    }

    public ArticleTag findArticleTag(Key key) {
        return em.find(ArticleTag.class, key);
    }

    public List<ArticleTag> listAll() {
        return (List<ArticleTag>) em.createNamedQuery("ArticleTag.findAll").getResultList();
    }

    public List<ArticleTag> listForCategory(String category) {
        ArticleCategory target = CategoryUtils.findCategory(category, em);
        if (target != null) {
            return target.getArticleTags();
        } else {
            return null;
        }
    }

    private void healtCheck(ArticleTag tag) throws DataIOException {
        if ((tag == null) || (tag.getTagName() == null)) {
            throw new DataIOException("Tag or tag name can't be a null");
        }
    }

    private void checkUnique(String tagName) throws DataIOException {
        /*
         * why not @Column(unique = true) on tagName ? , - because
         * java.lang.UnsupportedOperationException: No support for uniqueness
         * constraints
         */
        if (findArticleTag(tagName) != null) {
            throw new DataIOException("Tag with given name already exist");
        }

    }
    
}
